<template>
  <div class="linkage-form">
    <!-- 表单 -->
    <a-form-model ref="ruleForm" :rules="formRules" :model="formData">
      <template v-for="(item, index) in formItems">
        <a-form-model-item
          v-if="formDataFieldState[item.name].show"
          :key="`form-item-${index}`"
          :label="item.chineseName"
          :prop="item.name"
        >
          <div class="field-value">
            <component
              :is="getCompType(item)"
              v-model="formData[item.name]"
              show-search
              v-bind="{
                ...item.props,
                ...(item.relyBind ? parseRelyBind(item.relyBind) : {}),
              }"
              :form-data="formData"
              :name="item.name"
              :label="item.label"
              :props="item.props"
              :size="item.size ? item.size : 'small'"
              :disabled="formDataFieldState[item.name].disable"
              :style="{ width: nodeSetting ? 'calc(100% - 28px)' : '100%' }"
              :filter-option="filterOption"
              @change="
                (EventData) => {
                  formItemChange(EventData, item.name, item)
                }
              "
              @select-all="
                (checkedList) => {
                  checkboxSelectAll(checkedList, item.name)
                }
              "
              @blur="
                () => {
                  formItemBlur(item.name, item)
                }
              "
            >
            </component>
            <!-- 参数项提示信息 -->
            <span v-if="nodeSetting" class="help-tips">
              <a-tooltip
                v-show="item.message"
                placement="top"
                :overlay-style="{ fontSize: '12px' }"
                :destroy-tooltip-on-hide="true"
              >
                <template slot="title">
                  <span>{{ item.message }}</span>
                </template>
                <a-icon-font type="iconbangzhu" />
              </a-tooltip>
            </span>
          </div>
        </a-form-model-item>
      </template>
    </a-form-model>
  </div>
</template>

<script lang="ts">
/**
 * 联动表单 - 渲染能力
 */
import { Vue, Component, Watch, Prop } from 'vue-property-decorator'
import { flatten, mapKeys, union, cloneDeep, findIndex } from 'lodash'
import CheckboxGroup from '@/components/studio/data/set-params/linkage-form/CheckboxGroup.vue'
import SliderWithInput from '@/components/studio/data/set-params/linkage-form/SliderWithInput.vue'
import RadioGroup from '@/components/studio/data/set-params/linkage-form/RadioGroup.vue'
import FormSwitch from '@/components/studio/data/set-params/linkage-form/FormSwitch.vue'

// 表单类型
const compMaps: any = {
  inputNumber: {
    component: 'a-input-number',
    props: {},
  },
  text: {
    component: 'a-input',
    props: {},
  },
  radioGroup: {
    component: 'radio-group',
    props: {},
  },
  checkboxGroup: {
    component: 'checkbox-group',
    props: {},
  },
  select: {
    component: 'a-select',
    props: {},
  },
  switch: {
    component: 'form-switch',
    props: {},
  },
  sliderWithInput: {
    component: 'slider-with-input',
    props: {},
  },
}

@Component({
  components: {
    CheckboxGroup,
    SliderWithInput,
    RadioGroup,
    FormSwitch,
  },
})
export default class LinkageForm extends Vue {
  @Prop({ type: Boolean, default: false }) nodeSetting!: boolean // 参数项提示信息
  @Prop({
    type: Array,
    default: () => {
      return []
    },
  })
  formItems!: any[] // 表单项
  @Prop({
    type: Array,
    default: () => {
      return []
    },
  })
  conditions!: any[] // 所有的联动条件及行为
  @Prop({
    type: Object,
    default: () => {
      return {}
    },
  })
  formData!: any // form 表单字段 及 值
  // 表单字段 状态
  public formDataFieldState: any = {}

  /**
   * 监听节点变化
   */
  @Watch('conditions', { deep: true, immediate: true })
  private changeSelectNode(conditions: any) {
    if (conditions) {
      this.initFormDataFieldState()
    }
  }

  /**
   * 初始化
   * 节点的显示配置， 及其他
   */
  public initFormDataFieldState() {
    // 清除下记录历史
    this.oldFormData = {}
    // 初始化一下所有的 表单字段 状态
    Object.keys(this.formData).forEach((key) => {
      this.$set(this.formDataFieldState, key, {
        show: true,
        disable: false,
      })
    })
    // console.log(this.formDataFieldState, this.formData)
    this.watchFormData(this.formData)
  }

  public oldFormData: any = {} // 用来做前后对比

  // 监听表单值变化
  @Watch('formData', { deep: true, immediate: true })
  public watchFormData(newFormData: any) {
    this.$nextTick(() => {
      // formData 有改变的字段
      const changedField = this.compareField(newFormData, this.oldFormData)
      // 遍历所有的 condition
      this.conditions.forEach((item: any) => {
        // 当前 condition 涉及到的 field 字段, 去重后的
        const involvedField: string[] = this.involvedField(item)
        // 对比 变化的form字段 和 当前 condition涉及的字段
        const triggerAction: boolean =
          union(involvedField, changedField).length !==
          involvedField.length + changedField.length
        // console.log(
        //   triggerAction,
        //   this.conditional(item.condition, newFormData),
        //   'this.conditional(item.condition, newFormData)'
        // )
        // 验证条件满足 包含 condition 成立
        if (triggerAction && this.conditional(item.condition, newFormData)) {
          item.actions.forEach((action: any) => {
            this.conditionAction(action)
          })
        }
      })
      this.oldFormData = cloneDeep(newFormData) // 记录上次的修改结果， watch deep 不能得到具体哪个字段改变
    })
  }

  // 每个联动条件立的所有 涉及到的字段 去重后
  public involvedField(condition: any): string[] {
    return union(
      flatten(
        condition.condition.map((orItem: any) => {
          return orItem.map((andItem: any) => {
            return andItem.field
          })
        })
      )
    )
  }

  // 字段对比， 返回有改变的字段 array
  public compareField(newFormData: any, oldFormData: any) {
    const changedField: string[] = []
    mapKeys(newFormData, (value: any, key: any) => {
      if (value !== oldFormData[key]) {
        changedField.push(key)
      }
    })
    return changedField
  }

  /**
   * 条件判断 （提供给 禁用/显隐）
   */
  public conditional(condition: any[], formData: any) {
    return condition.some((orItem: any) => {
      return orItem.every((andCondition: any) => {
        switch (andCondition.condition) {
          // eq|=, ne|!=, gt|>, lt|<, ge|>=, le|<=
          case 'eq':
            return formData[andCondition.field] === andCondition.value
          case 'ne':
            return formData[andCondition.field] !== andCondition.value
          case 'gt':
            return formData[andCondition.field] > andCondition.value
          case 'lt':
            return formData[andCondition.field] < andCondition.value
          case 'ge':
            return formData[andCondition.field] >= andCondition.value
          case 'le':
            return formData[andCondition.field] <= andCondition.value
          case 'include':
            return formData[andCondition.field].include(andCondition.value)
          default:
            return false
        }
      })
    })
  }

  /**
   * 条件成立 action 分类执行
   */
  public conditionAction(action: any) {
    const { type, target } = action
    switch (type) {
      case 'show':
        this.formDataFieldState[target].show = true
        break
      case 'hidden':
        this.formDataFieldState[target].show = false
        break
      case 'enable':
        this.formDataFieldState[target].disable = false
        break
      case 'disable':
        this.formDataFieldState[target].disable = true
        break
      case 'changeValue':
        this.formData[target] = action.value
        break
      case 'changeOptions': {
        const fieldIndex: number = findIndex(this.formItems, ['name', target])
        if (action.value) {
          this.formData[target] = action.value
        }
        this.formItems[fieldIndex].props.options = action.options
        break
      }
      default:
        break
    }
  }

  public formItemChange(eventData: any, key: string, item: any) {
    console.log('change', eventData, key, item)
    // setTimeout(() => {
    //   if(item.type === 'radioGroup') {
    //     const { value } = eventData.target
    //     this.$emit('change', value, key, item)
    //   } else {
    //     this.$emit('change', eventData, key, item)
    //   }
    // }, 100)
  }

  /**
   * 复选框全选
   */
  public checkboxSelectAll(checkedList: any[], key: string) {
    this.formData[key] = checkedList
  }

  public formItemBlur(key: string, item: any) {
    console.log(key, item)
  }

  /**
   * 取消修改
   */
  public cancelChange() {
    this.initFormDataFieldState()
    this.$emit('cancel')
  }

  // 不同的字段项
  public getCompType(item: any): string {
    const result = 'a-input'
    if (compMaps[item.type]) {
      return compMaps[item.type].component
    }
    return result
  }

  /**
   * 依赖绑定
   */
  public parseRelyBind(relyBind: any) {
    const bind: any = relyBind.map((bindItem: any) => {
      if (this.formData[bindItem.field]) {
        return {
          [bindItem.name]: this.formData[bindItem.field],
        }
      }
      return {}
    })
    let bindObject: any = {}
    bind.forEach((item: any) => {
      bindObject = { ...bindObject, ...item }
    })
    return bindObject
  }

  /**
   * 表单验证规则
   */
  public get formRules() {
    const rules: any = {}
    this.formItems.forEach((item: any) => {
      if (item.rules) {
        rules[item.name] = item.rules
      }
    })
    return rules
  }

  // 列选项搜索功能
  public filterOption(input: any, option: any) {
    return option.componentOptions.children[0].text
      .toLowerCase()
      .includes(input.toLowerCase())
  }
}
</script>

<style lang="less" scoped>
/* 内容 */
.linkage-form {
  /deep/.ant-form-item {
    margin-bottom: 16px;

    .ant-form-item-label {
      height: 17px;
      line-height: 17px;
      margin-bottom: 6px;

      label {
        color: #5d637e;
        font-size: 12px;
        letter-spacing: 0.86px;
        max-width: 100%;
      }
    }
  }

  .field-value {
    //height: 50px;
    display: flex;
    justify-content: space-between;

    /* 帮助 */
    .help-tips {
      align-items: center;
      cursor: pointer;
      display: flex;
      font-size: 18px;
      justify-content: center;
      line-height: 100%;
      padding: 4px;
      width: 20px;
    }
  }
}
</style>
